﻿using System.Collections.Immutable;
using System.Diagnostics;
using DimensionsHelper.LanguageService.Protocol;
using DimensionsHelper.LanguageService.Symbols;

namespace DimensionsHelper.LanguageService.Syntax;

/// <summary>
///     使用<see cref="Lexer" />对代码文本进行拆解，此类保存拆解后的
///     文本结点列表。
/// </summary>
[DebuggerDisplay("{Uri.ToString(),nq}")]
public class ProcessedFile : PreNodeList
{
    private readonly SourceTextFile _source;
    private ImmutableArray<SyntaxComment>? _comments;
    private List<Directive>? _directives;
    private SourceNode? _endOfFileToken;
    private List<FileInclude>? _includeDirectives;
    private List<MacroExpansion>? _macroExpansions;
    private List<Macro>? _macros;

    internal ProcessedFile(SourceTextFile source) :
        base(null, SyntaxKinds.ProcessedFile)
    {
        _source = source;
        MacroSymbols = new SourceSymbolTable();
        base.SetUri(source.Uri);
        FileType = source.Type;
        TimeStamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
    }


    /// <summary>
    ///     获取当前文件的文本内容。
    /// </summary>
    public override string Text => _source.GetText();


    /// <summary>
    ///     预处理完成时的时间戳
    /// </summary>
    public long TimeStamp { get; internal set; }


    /// <summary>
    ///     文件起始位置。
    /// </summary>
    public override int Start => 0;

    /// <summary>
    ///     文件文本长度。
    /// </summary>
    public override int Width => Text.Length;


    /// <summary>
    ///     如果文件开头带有 <see cref="DirectiveKinds.MetadataFile" /> 的注释指令，
    ///     返回<see langword="true" />
    /// </summary>
    public bool HasMetadataDirective { get; private set; }


    /// <summary>
    ///     如果文件带有 <see cref="DirectiveKinds.IgnoreTypeError" /> 的注释指令，
    ///     返回<see langword="true" />
    /// </summary>
    public bool HasIgnoreTypeErrorDirective { get; private set; }


    /// <summary>
    ///     预处理后得到的文件类型，此属性收到代码文件内容的影响，例如，
    ///     在开头遇到<see cref="DirectiveKinds.MetadataFile" />类型的
    ///     注释指令，就会将此属性更改为<see cref="SourceFileType.Metadata" />。
    /// </summary>
    public SourceFileType FileType { get; private set; }


    /// <summary>
    ///     预处理中的宏符号表
    /// </summary>
    public SourceSymbolTable MacroSymbols { get; }


    /// <summary>
    ///     文本文件的结束位置
    /// </summary>
    public SourceNode? EndOfFileToken
    {
        get => _endOfFileToken;
        internal set => SetNullableFieldValue(ref _endOfFileToken, null, value);
    }


    /// <summary>
    ///     将注释指令节点添加到子节点列表和指令列表中。
    /// </summary>
    internal void AddDirective(Directive directive)
    {
        AddNode(directive);
        (_directives ??= []).Add(directive);

        switch (directive.DirectiveKind)
        {
            // <metadata />
            case DirectiveKinds.MetadataFile:
                HasMetadataDirective = true;
                FileType = SourceFileType.Metadata;
                break;
            // <ignore-type-error />
            case DirectiveKinds.IgnoreTypeError:
                HasIgnoreTypeErrorDirective = true;
                break;
            // <include file="FILE_NAME"/>
            case DirectiveKinds.Include when directive.SourceFile != null:
                _source.IncludeFile(directive.SourceFile);
                break;
            // <reference file="FILE_NAME" name="MACRO_NAME"/>
            default:
            {
                string filePath = DocumentUri.UriToFsPath(directive.Uri, true);
                if (!string.IsNullOrEmpty(filePath) &&
                    !IsNullOrDefault(directive.RelativeNameNode) &&
                    _source.Manager.TryLoadSourceFile(
                        _source.GetFullPath(filePath),
                        out SourceTextFile? source))
                {
                    source.IncludeFile(_source);
                }

                break;
            }
        }
    }


    /// <summary>
    ///     获取文件中开头的注释指令
    /// </summary>
    /// <returns></returns>
    public ImmutableArray<Directive> GetDirectives()
    {
        return _directives?.ToImmutableArray() ?? ImmutableArray<Directive>.Empty;
    }


    /// <summary>
    ///     获取此文件中的宏定义列表
    /// </summary>
    /// <returns></returns>
    public ImmutableArray<Macro> GetMacros()
    {
        return _macros?.ToImmutableArray() ?? ImmutableArray<Macro>.Empty;
    }


    internal void AddMacro(Macro macro)
    {
        _macros ??= [];
        _macros.Add(macro);
    }


    /// <summary>
    ///     此文件引用的文件列表
    /// </summary>
    public ImmutableArray<FileInclude> GetFileIncludeDirectives()
    {
        return _includeDirectives?.ToImmutableArray() ?? ImmutableArray<FileInclude>.Empty;
    }


    /// <summary>
    ///     引用指定的文件，同时将#include指令节点加入子节点列表和引用列表中。
    /// </summary>
    public bool AddIncludeDirective(FileInclude include)
    {
        _includeDirectives ??= [];
        _includeDirectives.Add(include);
        AddNode(include);
        return include.Source == null || _source.IncludeFile(include.Source);
    }


    /// <summary>
    ///     预处理中宏扩展节点列表。
    /// </summary>
    public ImmutableArray<MacroExpansion> GetMacroExpansions()
    {
        return _macroExpansions?.ToImmutableArray() ?? ImmutableArray<MacroExpansion>.Empty;
    }


    internal void AddMacroExpansion(MacroExpansion expansion)
    {
        _macroExpansions ??= [];
        _macroExpansions.Add(expansion);
    }


    /// <summary>
    ///     文件在预处理阶段遇到的错误列表。
    /// </summary>
    public override IEnumerable<DiagnosticInfo> GetDiagnostics()
    {
        return this.GetDiagnosticStorage();
    }


    internal void SetComments(in Span<SyntaxComment> comments)
    {
        _comments = comments.ToImmutableArray();
    }


    /// <summary>
    ///     获取预处理文件中的注释列表
    /// </summary>
    public ImmutableArray<SyntaxComment> GetComments()
    {
        return _comments ?? ImmutableArray<SyntaxComment>.Empty;
    }
}